package com.seewo.dubbo.debugger.admin.aop;

import com.seewo.dubbo.debugger.admin.aop.express.DebugExpressContext;
import com.seewo.dubbo.debugger.admin.entity.Debug;
import com.seewo.dubbo.debugger.admin.service.IDebugService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Date;

/**
 * debug方法的切面
 *
 * @author chenliangliang
 * @date 2018/7/29
 */
@Slf4j
@Component
@Aspect
public class DebugAspect {

    @Autowired
    private IDebugService debugService;

    @Autowired
    private ThreadPoolTaskExecutor executor;

    @Autowired
    private DebugExpressContext expressContext;

    private ParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();


    @Pointcut("@annotation(com.seewo.dubbo.debugger.admin.aop.Debug)")
    public void debugPointcut() {
    }


    @Around("debugPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        //异步更新t_debug表
        executor.execute(() -> handleDebug(joinPoint, result, start));
        return result;
    }

    private void handleDebug(ProceedingJoinPoint joinPoint, Object result, long start) {
        long time = System.currentTimeMillis() - start;

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        String[] parameterNames = discoverer.getParameterNames(method);
        Object[] parameters = joinPoint.getArgs();
        for (int i = 0, l = parameterNames.length; i < l; i++) {
            expressContext.addVariable("$" + String.valueOf(i + 1), parameters[i]);
            expressContext.addVariable(parameterNames[i], parameters[i]);
        }
        expressContext.addVariable("returnObject", result);

        com.seewo.dubbo.debugger.admin.aop.Debug debugAnnotation = method.getAnnotation(com.seewo.dubbo.debugger.admin.aop.Debug.class);

        String methodIdStr = debugAnnotation.methodId();
        if (StringUtils.isNotEmpty(methodIdStr)) {
            methodIdStr = expressContext.parseSpel(methodIdStr).toString();
        }

        String flag = debugAnnotation.result();
        if (StringUtils.isNotEmpty(flag)) {
            flag = expressContext.parseSpel(flag).toString();
        }

        try {
            Integer methodId = Integer.valueOf(methodIdStr);
            Debug debug = new Debug();
            debug.setCMethodId(methodId);
            debug.setCResult("true".equals(flag) ? 1 : 0);
            debug.setCTime(String.valueOf(time));
            debug.setCCreateTime(new Date());

            if (!debugService.insert(debug)) {
                log.warn("aop插入debug表失败：" + debug);
            }
        } catch (Exception e) {
            log.warn("数据格式或插入数据异常", e);
        }
        for (int i = 0, l = parameterNames.length; i < l; i++) {
            expressContext.removeVariable("$" + String.valueOf(i + 1));
            expressContext.removeVariable(parameterNames[i]);
        }
        expressContext.removeVariable("returnObject");
    }

}
